/**
 * Charting Connection Logic
 *
 */

function ChartingConnection() {
  this.reconnectTimeout = 2000;
  this.retryCount = 0;
  this.chartSocket = null;
  this.rangeSocket = null;
  this.lastHearbeatTime = 0;
  this.HEARTBEAT_INTERVAL = 30000;
  this.MAX_MISSED_HEARTBEAT = 3;
  this.heartbeatTimer;
  this.source;
  if (getParameterByName("source")) {
      source = getParameterByName("source");
  }  

  this.onMessage = null;
  this.disconnected = false;
  this._sendQueue = [];
}


/**
 * { function_description }
 *
 * @param      {(Function|WebSocket|string)}  urls           The urls
 * @param      {string}                       websocketHost  The websocket host
 * @param      {string}                       websocketPort  The websocket port
 * @param      {<type>}                       command        The command
 * @param      {boolean}                      isReconnect    Indicates if reconnect
 */
ChartingConnection.prototype.connect = function (urls, websocketHost, websocketPort, command, isReconnect) {
  if ( this.disconnected ) {
    return;
  }
  if(command != null) {
    var cmd = JSON.stringify(command);
  }

  if ( this.chartSocket == null) {
    if (urls != null && urls.length > 0) {
      if(cmd != null) {
        var index = this.retryCount % urls.length;
        var url = "ws://" + urls[index] + "/";
      }else{
        var url = urls;
      }
    }
    logger.info( "URLS: ", url );
    if (!isValued(url)) {
      if (!isValued(websocketHost) || !isValued(websocketPort)) {
        logger.log("Missing host/port");
        return;
      }
      url = "ws://" + websocketHost + ":" + websocketPort + "/";
    }
    logger.log("Requesting data from ChartingViewServer on ", url);

    //this remapping fun
    var chartingConnection = this;

    this.chartSocket = new WebSocket(url);
    this.chartSocket.onopen = function(event) {
      if (isReconnect) {
        chartingConnection.retryCount = 0;
        chartingConnection.reconnectTimeout = 2000;
      }
      logger.log("Chart Socket opened");
      //TODO add callback logic for clients
      
      if(cmd !=null ) {
        chartingConnection.chartSocket.send(cmd);
      } 
      if( chartingConnection.source == "Mobile") {
        chartingConnection.lastHearbeatTime = new Date();
        chartingConnection.heartbeatTimer = setInterval(checkHeartbeat, HEARTBEAT_INTERVAL);
      }
      while ( chartingConnection._sendQueue.length > 0 ) {
        var message = chartingConnection._sendQueue.shift();
        chartingConnection.send( message );
      }

    };
    this.chartSocket.onclose = function(event) {
      logger.log("Chart Socket closed", event);
      //TODO add close callback
      chartingConnection.chartSocket = null;
      if( chartingConnection.heartbeatTimer != null){
        clearInterval (chartingConnection.heartbeatTimer);
      }
      chartingConnection.reconnect(urls, websocketHost, websocketPort, command);
    };
    this.chartSocket.onerror = function(event) {
      logger.log("Unable to connect to websocket server");
    };
    this.chartSocket.onmessage = function( event ) {
      try{
        var json = JSON.parse(event.data);
        chartingConnection.lastHearbeatTime = new Date();
      } catch( e ) {
        logger.warn( "Bad Message ", event.data);
      }
      if ( chartingConnection.onMessage ) {
        chartingConnection.onMessage.call( chartingConnection, json );
      }
    }
  } else {
    this.send(cmd);
  }
}

ChartingConnection.prototype.send = function( command ) {
    if ( this.chartSocket != null  && this.chartSocket.readyState == this.chartSocket.OPEN && command != null) {
      this.chartSocket.send( JSON.stringify( command ) );
    } else {
      this._sendQueue.push( command );
      logger.debug("Chart socket is not open..queueing");
    }
}

ChartingConnection.prototype.disconnect = function () {
  console.log("Closing Sockets.")
  this.disconnected = true;
  if ( this.rangeSocket) {
    this.rangeSocket.close();
    delete this.rangeSocket;
    this.rangeSocket = null;
  }
  if ( this.chartSocket) {
    this.chartSocket.close();
    delete this.chartSocket;
    this.chartSocket = null;
  }
}


ChartingConnection.prototype.reconnect = function (urls, websocketHost, websocketPort, command) {
  var self = this;
  if (self.retryCount == 0){
    // Adding a cool down period on before reconnecting after disconnect
    var coolDownTime = NUMBER_UTILS.randomInterval(1, 10) * 1000;
    logger.log ("Cool down ... Waiting for " + coolDownTime/1000 + " seconds before attempting to reconnect");
    setTimeout ( self.reconnect, coolDownTime, urls, websocketHost, websocketPort, command, true);
    this.retryCount++;
  } else {
    logger.log("Reconnecting ... retry # " + self.retryCount + " in " + self.reconnectTimeout / 1000 + " seconds");
    setTimeout( self.connect, self.reconnectTimeout, urls, websocketHost, websocketPort, command, true);
    this.retryCount++;
    if ( self.reconnectTimeout < self.MAX_RECONNECT_TIMEOUT) {
      self.reconnectTimeout *= 2;
    } else {
      self.reconnectTimeout = 2000;
    }
  }
}


ChartingConnection.prototype.getExcelWebsocketURL = function() {
  var websocketHost = "127.0.0.1";
  var websocketPort = "10080";
  var websocketPath = "range";
  var processId = "0";
  var protocol = "ws";

  if (getParameterByName("host")) {
    websocketHost = getParameterByName("host");
  }
  if (getParameterByName("port")) {
    websocketPort = getParameterByName("port");
  }
  if (getParameterByName("path")) {
    websocketPath = getParameterByName("path");
  }
  if (getParameterByName("processId")) {
    processId = getParameterByName("processId");
  }
  if (getParameterByName("protocol")) {
    protocol = getParameterByName("protocol");
  }
  if (getParameterByName("source")) {
    source = getParameterByName("source");
  }

  var url = protocol + "://" + websocketHost + ":" + websocketPort + "/" + encodeURIComponent(websocketPath);
  url = url + window.location.search;
  if (url.indexOf('?') < 0) url = url + "?1=1";
  return url;
}


ChartingConnection.prototype.requestData = function(command, callback) {
  if ( this.disconnected ) return;
  var urls = ARRAY_UTILS.shuffle(command.urls);
  var websocketHost = command.host;
  var websocketPort = command.port;
  if (websocketHost == null && isValued(command.parameters)) {
    websocketHost = command.parameters.host;
    websocketPort = command.parameters.port;
  }
  if (!isValued( urls) && ( !isValued(websocketHost) || !isValued(websocketPort) ) ) {
    var testingFlag = getParameterByName( "testing" );
    if ( isValued( testingFlag ) && testingFlag.toLowerCase() == "true") {
      logger.info("Only range socket exists, command socket will be rangesocket" );
      this.chartSocket = this.rangeSocket;
    } else {
      logger.info( "Waiting for configuration command" );
      var undef;
      if ( callback != undef && callback != null) {
        callback.apply();
      }
      return;
    }
  } else if ( this.chartSocket == this.rangeSocket ) {
    this.chartSocket = null;
  }
  this.connect(urls, websocketHost, websocketPort, command, false);
}

var _chartingConnection = new ChartingConnection();
